#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <libspe2.h>
#include <ppu_intrinsics.h>
#include <pthread.h>
#include "../ubicus.h"
#include <string.h>
#include <libsync.h>//pour le mutex
#include <stdint.h>//pour le mutex



/* Here we define a data structure to store the data that need to be
 * passed into the pthread function that starts the spe thread */
typedef struct _ppu_thread_data 
{
  spe_context_ptr_t spe_context;        //spe context
  void* control_block_ptr;              //pointer to the control block
} ppu_thread_data_t;

/* this is the pointer to the SPE code */
extern spe_program_handle_t ubicus_spu;


/* PPE pthread function that starts the SPE thread */
void *ppu_pthread_function(void *argp) {

  ppu_thread_data_t *thread_data = (ppu_thread_data_t *)argp;

  spe_context_ptr_t ctx;

  unsigned int entry = SPE_DEFAULT_ENTRY;
 
  ctx = thread_data->spe_context;
 /*  start the SPE thread. We want to pass the pointer to the control block to the SPE */
  if (spe_context_run(ctx, &entry, 0, thread_data->control_block_ptr, NULL, NULL) < 0) {
    perror ("Failed running context");
    exit (1);
  }
  pthread_exit(NULL);
}

void concatener(int nombreDePas)
{

	int i; 
	FILE * script =NULL;
	script = fopen("./resultat/cat.sh","w");

	if (script!=NULL)
	{
		fprintf(script, "rm ../flexouille/sortie.bin\ncat ");
		for( i=0; i<nombreDePas; i++)
		{	
			fprintf(script, "%i ", i);
		}

		fprintf(script, "> ../flexouille/sortie.bin");		
	}
	fclose(script);
	system("cd resultat\nchmod 777 ./cat.sh\n./cat.sh");

}

unsigned int lock_value;
static mutex_ea_t LOCKER_EA;

int main()
{
  int i, num_spe_threads, rc, itmp;
  int j = 0;
  int nombreDePas, nombreDePasFait;
  spe_context_ptr_t spe_contexts[MAX_NUM_SPE_THREADS];
  pthread_t spe_threads[MAX_NUM_SPE_THREADS];
  control_block_t* control_blocks[MAX_NUM_SPE_THREADS];
  ppu_thread_data_t ppu_thread_data[MAX_NUM_SPE_THREADS];
  PRECISION ftmp;
 
  LOCKER_EA = (mutex_ea_t)(uintptr_t)(&lock_value);
  mutex_init(LOCKER_EA);


  /* Determine the number of SPE threads to create. */
  num_spe_threads = spe_cpu_info_get(SPE_COUNT_USABLE_SPES, -1);
  if (num_spe_threads > MAX_NUM_SPE_THREADS) num_spe_threads = MAX_NUM_SPE_THREADS;


  nombreDePas = ((A_MAX - A_MIN)/INTERVALLE_A);
  for (i = 0; i < num_spe_threads; i++) 
  {
    /* create control block */
    rc = posix_memalign ((void**)(&control_blocks[i]), 128, sizeof (control_block_t));
    if (rc != 0)
    {
      fprintf (stderr, "Failed allocating space for control block\n");
      exit (1);
    }
 
    /* load the control block  */ 
    control_blocks[i]->idA = i;
    control_blocks[i]->a_min = A_MIN + (i * INTERVALLE_A);
    control_blocks[i]->addr = (unsigned long long)&LOCKER_EA;

    /* Create SPE threads to execute 'ubicus_spu'. */
    spe_contexts[i]= spe_context_create (0, NULL);
    if (spe_contexts[i] == NULL)
    {
      perror ("Failed creating SPE context");
      exit (1);
    }

    /* Load SPE program into context */
    if (spe_program_load (spe_contexts[i], &ubicus_spu))
    {
      perror ("Failed loading SPE program");
      exit (1);
    }

    /* load ppu_thread_data */
    ppu_thread_data[i].spe_context = spe_contexts[i];
    ppu_thread_data[i].control_block_ptr = control_blocks[i];

    /* Create SPE thread */
    if (pthread_create (&spe_threads[i], NULL, &ppu_pthread_function, &ppu_thread_data[i]))
    {
      perror ("Failed creating SPE thread");
      exit (1);
    }

    /*** SPE is executing ***/
  }
  nombreDePasFait = num_spe_threads;
  
  
  
  while (nombreDePasFait <= nombreDePas)
  {
  	
    for (i = 0; i < num_spe_threads; i++)
  	{
	    /* Wait for SPE thread to complete execution */
	    if (pthread_join (spe_threads[i], NULL))
	    {
	      perror ("Failed pthread_join");
	      exit (1);
	    }
	    
		    /* Destroy context */
		    if (spe_context_destroy (spe_contexts[i]) != 0)
		    {
		      perror ("Failed destroying SPE context");
		      exit (1);
		    }
		    free (control_blocks[i]);
		
	    // si il reste des calcul a faire alors
	    if(nombreDePasFait <= nombreDePas)
	    {
		    /* create control block */
		    rc = posix_memalign ((void**)(&control_blocks[i]), 128,  sizeof (control_block_t));
		    if (rc != 0)
		    {
		      fprintf (stderr, "Failed allocating space for control  block\n");
		       exit (1);
		    }
		 
		    /* load the control block  */ 
		    control_blocks[i]->idA = nombreDePasFait;
		    control_blocks[i]->a_min = A_MIN + (nombreDePasFait * 
		    INTERVALLE_A);
		    control_blocks[i]->addr = (unsigned long long)&LOCKER_EA;
		
		    /* Create SPE threads to execute 'ubicus_spu'. */
		    spe_contexts[i]= spe_context_create (0, NULL);
		    if (spe_contexts[i] == NULL)
		    {
		      perror ("Failed creating SPE context");
		      exit (1);
		    }
		
		    /* Load SPE program into context */
		    if (spe_program_load (spe_contexts[i], &ubicus_spu))
		    {
		      perror ("Failed loading SPE program");
		      exit (1);
		    }
		
		    /* load ppu_thread_data */
		    ppu_thread_data[i].spe_context = spe_contexts[i];
		    ppu_thread_data[i].control_block_ptr = control_blocks[i];
		
		    /* Create SPE thread */
		    if (pthread_create (&spe_threads[i], NULL, 
		    &ppu_pthread_function, &ppu_thread_data[i]))
		    {
		      perror ("Failed creating SPE thread");
		      exit (1);
		    }
		
		    /*** SPE is executing ***/
	    	printf("%i/%i \n", nombreDePasFait, nombreDePas);
	    	nombreDePasFait ++;
			
	    	j=i;
	
		}
	    
	 }
   }
       for (i = 0; i < j; i++)
  	{
	    /* Wait for SPE thread to complete execution */
	    if (pthread_join (spe_threads[i], NULL))
	    {
	      perror ("Failed pthread_join");
	      exit (1);
	    }
	    
		/* Destroy context */
		if (spe_context_destroy (spe_contexts[i]) != 0)
		{
		  perror ("Failed destroying SPE context");
		  exit (1);
		}
		free (control_blocks[i]);
	}	
	//tri des resultats vers sortie.bin
	//creerEntete(); comment!?
	concatener(nombreDePas);
//	ecriture des valeur de ubicus.h
	
	itmp = MAX_NUM_SPE_THREADS;
	printf("nombre de threads : %i\n", itmp);
	ftmp = A_MIN;
	printf("amin : %f", ftmp);
	ftmp = A_MAX;
	printf(" amax : %f\n", ftmp);
	ftmp = B_MIN;
	printf("bmin : %f ", ftmp);
	ftmp = B_MAX;
	printf("bmax : %f\n", ftmp);
	ftmp = PAS_A;
	printf("pasa : %f\n", ftmp);
	ftmp = PAS_B;
	printf("pasb : %f\n", ftmp);
	ftmp = x_min;
	printf("xmin : %f ", ftmp);
	ftmp = x_max;
	printf("xmax : %f\n", ftmp);
	ftmp = y_min;
	printf("ymin : %f ", ftmp);
	ftmp = y_max;
	printf("ymax : %f\n", ftmp);
	itmp = NB_VAL_A;
	printf("nbvala : %i ", itmp);
	itmp = NB_VAL_B;
	printf("nbvalb : %i\n", itmp);
	ftmp = INTERVALLE_A;
	printf("intervallea : %f\n", ftmp);



	return (0);
}
